#include<strings.h>
#include<string.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<arpa/inet.h>
#include<arpa/inet.h>
#include<iostream>
#include<stdlib.h>
using namespace std;

class Sock
{
public:
  //1.创建套接字 ->返回的是文件描述符
  static int Socket()
  {
    //协议家族:AF_INET 表示网络通信
    //服务类型:SOCK_STREM 因为是TCP通信 ->选择流式服务
    //协议类别:TCP / UDP 填0,让OS根据前两个参数自动推导
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
        perror("socket");
        exit(1);
    }
    return sock;
  }
  //2.绑定 参数：要绑定的文件描述符 端口号  云服务器不需要ip地址,不建议绑定固定的ip 
  static void Bind(int sockfd,uint16_t port)
  {
   struct sockaddr_in local; 
   memset(&local,0,sizeof(local));
   local.sin_family = AF_INET;
   local.sin_port = htons(port);//主机序 -> 网络字节序
   local.sin_addr.s_addr = INADDR_ANY;
    if(bind(sockfd,(struct sockaddr*)&local,sizeof(local)) < 0)
    {
      perror("bind");
      exit(2);
    }
  }
  //3.设置套接字为监听状态(允许客户端连接)
  static void Listen(int sockfd)
  {
    //第一个参数:要设置为监听状态的套接字 第二个参数:全连接队列的最大长度
    //没有被服务端处理的连接会被放到全连接队列当中
    if(listen(sockfd,5) < 0)
    {
      perror("listen");
      exit(3);
    }
  }
  //4.监听套接字当中获取连接 返回的是提供服务的文件描述符
  static int Accept(int sockfd)
  {
    struct sockaddr_in peer;//保存发起连接的客户端的网络相关信息
    socklen_t len = sizeof(peer);//期望获取的该结构体的大小--输出型参数 
    int fd = accept(sockfd,(struct sockaddr*)&peer,&len);
    if(fd >= 0) //获取连接成功
    {
      //打印客户端的ip和端口号,观察是谁连接了当前服务器
      uint16_t port = ntohs(peer.sin_port);//端口号由网络字节序->主机字节序
      std::string ip = inet_ntoa(peer.sin_addr);//整数ip->字符串ip 注意inet_ntoa的参数类型是struct in_addr
      cout <<"get a new link port:" << port <<"  ip: " << ip << endl;
      return fd;
    }
    return -1;
    
  }
  //5.客户端发起连接  参数:通过该套接字发起连接请求 向谁发送请求(ip+端口号)
  static void Connect(int sock, std::string ip, uint16_t port)
  {
      struct sockaddr_in server;//要连接哪个服务器,填入对应的信息
      memset(&server, 0, sizeof(server));

      server.sin_family = AF_INET;//协议家族
      server.sin_port = htons(port);//主机转网络
      server.sin_addr.s_addr = inet_addr(ip.c_str());//字符串IP->整数IP

      //连接
      if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0)
      {
          cout << "Connect Success!" << endl;
      }
      else
      {
          cout << "Connect Failed!" << endl;
          exit(5);
      }
  }
};
